using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.Threading;

namespace MuggleTactics
{   
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        #region GlobalObjects
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        SpriteFont spriteFont;
        Texture2D harrySprite;
        Texture2D backLevel;
        Texture2D hermioneSprite;
        Texture2D ronSprite;
        Texture2D tileMask;
        Texture2D badGuy;
        Texture2D menuBackground;
        Texture2D unitInfoBackground;
        Battle battle;
        KeyboardState currentKeyboardState;
        KeyboardState oldKeyboardState;
        MouseState previousState;
        List<Tile> tiles;

        Point frameStart = new Point(0, 0);


        #endregion

        public enum SelectionState
        {
            actionSelection,
            tileSelection,
            spellSelection,
            itemSelection
        }
        public static SelectionState selectionState;

        #region getters
        public static SelectionState getSelectionState()
        {
            return selectionState;
        }

        public static SelectionState getActionSelectionState()
        {
            return SelectionState.actionSelection;
        }

        public static SelectionState getTileSelectionState()
        {
            return SelectionState.tileSelection;
        }

        public static SelectionState getSpellSelectionState()
        {
            return SelectionState.spellSelection;
        }

        public static SelectionState getItemSelectionState()
        {
            return SelectionState.itemSelection;
        }
        #endregion

        #region setters
        public static void setSelectionState(SelectionState state)
        {
            selectionState = state;
        }
        #endregion

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            graphics.PreferredBackBufferHeight = 480;
            graphics.PreferredBackBufferWidth = 720;
            graphics.ApplyChanges();
            this.IsMouseVisible = true;
            tiles = loadMapOneTiles();

            List<Tile> tileList = new List<Tile>();
            List<Unit> unitList = new List<Unit>();

            tileList = loadMapOneTiles();
            unitList = populateUnits(tileList);

            battle = new Battle(unitList, tileList);

            currentKeyboardState = new KeyboardState();

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            backLevel = Content.Load<Texture2D>("Images/BattleMap1");
            //harrySprite = Content.Load<Texture2D>(harry.texturePath);
            harrySprite = Content.Load<Texture2D>("Images/Harry_Potter_sprite");
            hermioneSprite = Content.Load<Texture2D>("Images/Hermione");
            ronSprite = Content.Load<Texture2D>("Images/Ron");
            spriteFont = Content.Load<SpriteFont>("Fonts/SpriteFont1");
            tileMask = Content.Load<Texture2D>("Images/tileMask");
            badGuy = Content.Load<Texture2D>("Images/badGuy");
            menuBackground = Content.Load<Texture2D>("Images/MainMenuBG");
            unitInfoBackground = Content.Load <Texture2D>("Images/Dropdown");

            // TODO: use this.Content to load your game content here
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            oldKeyboardState = currentKeyboardState;
            currentKeyboardState = Keyboard.GetState();

            switch (selectionState)
            {
                case SelectionState.actionSelection:
                    //Console.WriteLine("action State"); // Prints out value to console

                    if (currentKeyboardState.IsKeyUp(Keys.D1) && (oldKeyboardState.IsKeyDown(Keys.D1)))
                    {
                        battle.setActionInput(Battle.ActionInput.Move);
                    }
                    else if (currentKeyboardState.IsKeyUp(Keys.D2) && (oldKeyboardState.IsKeyDown(Keys.D2)))
                    {
                        battle.setActionInput(Battle.ActionInput.Spell);
                    }
                    else if (currentKeyboardState.IsKeyUp(Keys.D3) && (oldKeyboardState.IsKeyDown(Keys.D3)))
                    {
                        battle.setActionInput(Battle.ActionInput.Item);
                    }
                    break;

                case SelectionState.tileSelection:
                    getMouseInput();
                    break;
                case SelectionState.spellSelection:
                    if (currentKeyboardState.IsKeyDown(Keys.D1))
                    {
                        battle.setUsableInput(1);
                    }

                    else if (currentKeyboardState.IsKeyDown(Keys.D2))
                    {
                        battle.setUsableInput(2);
                    }

                    else if (currentKeyboardState.IsKeyDown(Keys.D3))
                    {
                        battle.setUsableInput(3);
                    }
                    break;
                case SelectionState.itemSelection:
                    break;
            }

            battle.update();
            foreach (Unit unit in battle.units)
            {
                if (unit.getName() == battle.getActiveUnit().getName())
                {
                    unit.Update(gameTime, Window.ClientBounds);
                }
            }

            base.Update(gameTime);
        }

        private void getMouseInput()
        {
            // When map is clicked the getIsOccupied value of that tile is returned
            MouseState mouseState = Mouse.GetState();
            if (mouseState.LeftButton == ButtonState.Pressed && previousState.LeftButton == ButtonState.Released)
            {
                Vector2 mouseClickLocation = new Vector2(mouseState.X, mouseState.Y);

                foreach (Tile t in battle.tiles)
                {
                    if (mouseClickLocation.X > t.getLocation().X
                        && mouseClickLocation.Y > t.getLocation().Y
                        && mouseClickLocation.X < t.getLocation().X + 48
                        && mouseClickLocation.Y < t.getLocation().Y + 48)
                    {

                        //Console.WriteLine(battle.tiles.IndexOf(t));
                        //Console.WriteLine(t.getIsOccupied().ToString()); // Prints out value to console 

                        //Console.WriteLine(battle.tiles.IndexOf(t) + " " + t.getIsOccupied().ToString()); // Prints out value to console 
                        //battle.setActionInput(Battle.ActionInput.Move);
                        battle.tileSelectionInRange(battle.tiles.IndexOf(t));
                        break;
                    }
                }
            }

            previousState = mouseState;
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            spriteBatch.Begin();
            spriteBatch.Draw(backLevel, Vector2.Zero, Color.White);
            spriteBatch.Draw(unitInfoBackground, new Vector2(10, 10), Color.White);
            spriteBatch.Draw(menuBackground, new Vector2(10, 390), Color.White);
            switch (selectionState)
            {
                case SelectionState.actionSelection:
                    drawActionMenu();
                    break;
                case SelectionState.itemSelection:
                    drawItemMenu();
                    break;
                case SelectionState.spellSelection:
                    drawSpellMenu();
                    break;
                case SelectionState.tileSelection:
                    drawSelectable();
                    break;
                default:
                    break;
            }

            //for (int i = 0; i < battle.units.Count; i++)
           
            
            //battle.units[0].Draw(gameTime, spriteBatch);
            //battle.units[1].Draw(gameTime, spriteBatch);

            for (int i = 0; i < battle.units.Count; i++)

            {
                battle.units[i].Draw(gameTime, spriteBatch);
            }

            drawUnitInfo();

            spriteBatch.End();
            base.Draw(gameTime);
        }

        public void test()
        {
            Spell spell1 = new Spell(-5, 2, "Incendio", StatsHelper.getHitPointsStat(), 3);
            Spell spell2 = new Spell(-5, 2, "Imperio", StatsHelper.getHitPointsStat(), 3);
            Spell spell3 = new Spell(-5, 2, "Crucio", StatsHelper.getHitPointsStat(), 3);
            Spell spell4 = new Spell(-5, 2, "Avada Kedavra", StatsHelper.getHitPointsStat(), 3);
            List<Spell> spells = new List<Spell>();
            spells.Add(spell1);
            spells.Add(spell2);
            spells.Add(spell3);
            spells.Add(spell4);

            Item item1 = new Item(5, 2, "Potion", StatsHelper.getHitPointsStat());
            Item item2 = new Item(10, 2, "Super Potion", StatsHelper.getHitPointsStat());
            List<Item> items = new List<Item>();
            items.Add(item1);
            items.Add(item2);

            BattleTest test = new BattleTest(populateUnits(battle.tiles), battle.tiles);
            test.start();
        }

        private List<Unit> populateUnits(List<Tile> tiles)
        {

            Spell spell1 = new Spell(-5, 50, "Imperio", StatsHelper.getHitPointsStat(), 3);
            Spell spell2 = new Spell(-5, 40, "Avada Kevadra", StatsHelper.getHitPointsStat(), 3);
            Spell spell3 = new Spell(-5, 80, "Incendio", StatsHelper.getHitPointsStat(), 3);
            Spell spell4 = new Spell(-5, 20, "Crucio", StatsHelper.getHitPointsStat(), 3);

            List<Spell> spells = new List<Spell>();
            spells.Add(spell1);
            spells.Add(spell2);
            spells.Add(spell3);
            spells.Add(spell4);

            Item item1 = new Item(5, 2, "Potion", StatsHelper.getHitPointsStat());
            Item item2 = new Item(10, 2, "Super Potion", StatsHelper.getHitPointsStat());
            List<Item> items = new List<Item>();
            items.Add(item1);
            items.Add(item2);

            List<Unit> unitList = new List<Unit>();
            Unit tempUnit; //tmp unit to pass into the array
            //create our three heroes

            //tempUnit = new Unit(this, Content.Load<Texture2D>("Images/Harry_Potter_sprite"),
            // new Vector2(10, 0), new Point(0, 3), new Point(33, 50), new Point(4, 4), 250);

            tempUnit = new Unit(this, Content.Load<Texture2D>("Images/Harry_Potter_sprite"), new Vector2(10, 0), frameStart, new Point(33, 49), new Point(4, 4), "Harry", 5, 4, 3, 4, true, spells, items);

            tempUnit.setIsPlayer(true);
            tiles[6].setPiece(tempUnit); //place in starting location, not 0 most likely
            unitList.Add(tempUnit);
            //Console.WriteLine(tiles[0].getIsOccupied().ToString());
            tempUnit = new Unit(this, Content.Load<Texture2D>("Images/Hermione"), new Vector2(10, 48), frameStart, new Point(33, 49), new Point(4, 4), "Hermione", 5, 4, 3, 4, true, spells, items);
            tiles[50].setPiece(tempUnit); //place in starting location, not 0 most likely

            unitList.Add(tempUnit);


            //Console.WriteLine(tiles[1].getIsOccupied().ToString());
            tempUnit = new Unit(this, Content.Load<Texture2D>("Images/Ron"), new Vector2(10, 96), frameStart, new Point(33, 49), new Point(4, 4), "Ron", 5, 4, 3, 4, true, spells, items);
            tiles[49].setPiece(tempUnit); //place in starting location, not 0 most likely

            unitList.Add(tempUnit);
            //Console.WriteLine(tiles[2].getIsOccupied().ToString());


            tempUnit = new Unit(this, Content.Load<Texture2D>("Images/BadGuy"), new Vector2(0, 0), frameStart, new Point(33, 49), new Point(4, 4), "BadGuy1", 5, 4, 3, 5, false, spells, items);
            tiles[36].setPiece(tempUnit);
            unitList.Add(tempUnit);

            //int enemyCount = 10;
            //for (int i = 0; i <= enemyCount; i++)
            //{
            //    //some more facny logic here to find starting location
            //    tempUnit = new Unit(this, Content.Load<Texture2D>("Images/BadGuy"),
            //    new Vector2(25, 25), new Point(0, 3), new Point(33, 50), new Point(4, 4));
            //    t[0].setPiece(tempUnit); //place in starting location, not 0 most likely
            //    unitList.Add(tempUnit);

            //}

            return unitList;
        }

        private List<Tile> loadMapOneTiles()
        {
            List<Tile> tiles = new List<Tile>(149);
            int x = 0;
            int y = 0;

            // Fills tiles with all tiles on map with locations
            for (int i = 0; i < 150; i++)
            {
                tiles.Add(new Tile(null, new Vector2(x, y)));
                x += 48;
                if (x >= 720)
                {
                    x = 0;
                    y += 48;
                }
            }

            #region setOccupiedTiles
            // Row 1
            tiles[0].setIsOccupied(true);
            tiles[1].setIsOccupied(true);
            tiles[2].setIsOccupied(true);
            tiles[3].setIsOccupied(true);
            tiles[4].setIsOccupied(true);
            tiles[5].setIsOccupied(true);
            tiles[9].setIsOccupied(true);
            tiles[10].setIsOccupied(true);
            tiles[11].setIsOccupied(true);
            tiles[12].setIsOccupied(true);
            tiles[13].setIsOccupied(true);
            tiles[14].setIsOccupied(true);

            // Row 2
            tiles[15].setIsOccupied(true);
            tiles[24].setIsOccupied(true);
            tiles[25].setIsOccupied(true);
            tiles[26].setIsOccupied(true);
            tiles[27].setIsOccupied(true);
            tiles[28].setIsOccupied(true);
            tiles[29].setIsOccupied(true);

            // Row 3
            tiles[30].setIsOccupied(true);
            tiles[39].setIsOccupied(true);
            tiles[40].setIsOccupied(true);
            tiles[41].setIsOccupied(true);
            tiles[42].setIsOccupied(true);
            tiles[43].setIsOccupied(true);
            tiles[44].setIsOccupied(true);

            // Row 4
            tiles[45].setIsOccupied(true);
            tiles[54].setIsOccupied(true);
            tiles[55].setIsOccupied(true);
            tiles[56].setIsOccupied(true);
            tiles[57].setIsOccupied(true);
            tiles[58].setIsOccupied(true);
            tiles[59].setIsOccupied(true);

            // Row 5
            tiles[60].setIsOccupied(true);
            tiles[61].setIsOccupied(true);
            tiles[62].setIsOccupied(true);

            // Row 6
            tiles[75].setIsOccupied(true);
            tiles[76].setIsOccupied(true);
            tiles[77].setIsOccupied(true);
            tiles[78].setIsOccupied(true);

            // Row 7
            tiles[91].setIsOccupied(true);
            tiles[92].setIsOccupied(true);
            tiles[93].setIsOccupied(true);
            tiles[94].setIsOccupied(true);
            tiles[104].setIsOccupied(true);

            // Row 8
            tiles[112].setIsOccupied(true);
            tiles[113].setIsOccupied(true);
            tiles[114].setIsOccupied(true);
            tiles[119].setIsOccupied(true);

            // Row 9
            tiles[121].setIsOccupied(true);
            tiles[122].setIsOccupied(true);
            tiles[123].setIsOccupied(true);
            tiles[124].setIsOccupied(true);
            tiles[134].setIsOccupied(true);

            // Row 10 
            tiles[135].setIsOccupied(true);
            tiles[136].setIsOccupied(true);
            tiles[137].setIsOccupied(true);
            tiles[138].setIsOccupied(true);
            tiles[139].setIsOccupied(true);
            tiles[140].setIsOccupied(true);
            tiles[141].setIsOccupied(true);
            tiles[142].setIsOccupied(true);
            tiles[143].setIsOccupied(true);
            tiles[144].setIsOccupied(true);
            tiles[145].setIsOccupied(true);
            tiles[146].setIsOccupied(true);
            tiles[147].setIsOccupied(true);
            tiles[148].setIsOccupied(true);
            tiles[149].setIsOccupied(true);

            #endregion

            return tiles;
        }

        private void drawUnitInfo()
        {
            int y = 20;
            int x = 25;
            foreach (Unit unit in battle.units) {
                spriteBatch.DrawString(spriteFont, unit.getHitPoints().ToString(), new Vector2(x, y), Color.Black);
                spriteBatch.DrawString(spriteFont, unit.getName(), new Vector2(x + 20, y), Color.Black);
                
                y += 20;
            }
        }

        private void drawActionMenu()
        {
            int y = 395;
            int x = 20;
            spriteBatch.DrawString(spriteFont, "Select an option", new Vector2(x, y), Color.Black);
            spriteBatch.DrawString(spriteFont, "1. Move", new Vector2(x, y += 20), Color.Black);
            spriteBatch.DrawString(spriteFont, "2. Spell", new Vector2(x, y += 20), Color.Black);
            spriteBatch.DrawString(spriteFont, "3. Item", new Vector2(x, y += 20), Color.Black);         
        }

        private void drawSpellMenu()
        {
            int y = 395;
            int x = 20;
            spriteBatch.DrawString(spriteFont, "Select an option", new Vector2(x, y), Color.Black);
            List<String> spellList = battle.getSpellNames();
            int height = 0;
            foreach (String spell in spellList)
            {
                height += 20;
                spriteBatch.DrawString(spriteFont, (spellList.IndexOf(spell) + 1) + ". " + spell, new Vector2(x, y + height), Color.Black);
            }
        }

        private void drawItemMenu()
        {
            int y = 395;
            int x = 20;
            spriteBatch.DrawString(spriteFont, "Select an option", new Vector2(x, y), Color.Black);
            List<String> itemList = battle.getActiveUnit().getItemNames();
            int height = 0;
            foreach (String item in itemList)
            {
                height += 20;
                spriteBatch.DrawString(spriteFont, (itemList.IndexOf(item) + 1) + ". " + item, new Vector2(x, y + height), Color.Black);
            }
        }

        private void drawSelectable()
        {
            List<int> tilesInRange = battle.tilesInRange;
            Vector2 tileLocation = new Vector2();
            foreach (int i in tilesInRange)
            {
                tileLocation = battle.tiles[i].getLocation();
                spriteBatch.Draw(tileMask, tileLocation, Color.White);
            }
        }
    }
}